Completed
Push — master ( 7c9428...586be1 )
by Dongxin
27s
created

mjsonviewer.js ➔ ... ➔ changeSiblings   C

Complexity

Conditions 10
Paths 14

Size

Total Lines 21

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 1
Metric Value
cc 10
c 1
b 0
f 1
nc 14
nop 3
dl 0
loc 21
rs 6.6746

How to fix   Complexity   

Complexity

Complex classes like mjsonviewer.js ➔ ... ➔ changeSiblings often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

1
//////////////////////////////////////////////////////////////////////////////////////
2
// Copyright © 2017 TangDongxin
3
//
4
// Permission is hereby granted, free of charge, to any person obtaining
5
// a copy of this software and associated documentation files (the "Software"),
6
// to deal in the Software without restriction, including without limitation
7
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
8
// and/or sell copies of the Software, and to permit persons to whom the
9
// Software is furnished to do so, subject to the following conditions:
10
//
11
// The above copyright notice and this permission notice shall be included
12
// in all copies or substantial portions of the Software.
13
//
14
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
16
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
17
// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
18
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
19
// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
20
// OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21
//////////////////////////////////////////////////////////////////////////////////////
22
23
// ===========================================
24
// JSON PARSER
25
// ===========================================
26
27
var bgColor, intColor, strColor, keyColor, defaultColor;
28
var fontStyle;
29
var strictOnly;
30
31
function onError(error) {
32
    console.log(error);
0 ignored issues
show
Debugging Code introduced by
console.log looks like debug code. Are you sure you do not want to remove it?
Loading history...
33
}
34
35
function onGot(result) {
36
    if (result[0]) {
37
        fontStyle    = result[0].fontStyle    || "Consolas";
38
        bgColor      = result[0].bgColor      || "#FDF6E3";
39
        intColor     = result[0].intColor     || "#657A81";
40
        strColor     = result[0].strColor     || "#2AA198";
41
        keyColor     = result[0].keyColor     || "#B58900";
42
        defaultColor = result[0].defaultColor || "#586E75";
43
44
        strictOnly   = result[0].strictOnly   || false;
45
    } else {
46
        fontStyle    = result.fontStyle       || "Consolas";
47
        bgColor      = result.bgColor         || "#FDF6E3";
48
        intColor     = result.intColor        || "#657A81";
49
        strColor     = result.strColor        || "#2AA198";
50
        keyColor     = result.keyColor        || "#B58900";
51
        defaultColor = result.defaultColor    || "#586E75";
52
53
        strictOnly   = result.strictOnly      || false;
54
    }
55
56
    var str, jsonpMatch, hovered, tag,
57
        chrome = this.chrome || this.browser,
58
        jsonRe = /^\s*(?:\[\s*(?=-?\d|true|false|null|["[{])[^]*\]|\{\s*"[^]+\})\s*$/,
59
        div = document.createElement("div"),
60
        body = document.body,
61
        first = body && body.firstChild,
62
        mod = /Mac|iPod|iPhone|iPad|Pike/.test(navigator.platform) ? "metaKey" : "ctrlKey",
0 ignored issues
show
Bug introduced by
The variable navigator seems to be never declared. If this is a global, consider adding a /** global: navigator */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
63
        rand = Math.random().toString(36).slice(2),
64
        HOV = "H" + rand,
65
        DIV = "D" + rand,
66
        KEY = "K" + rand,
67
        STR = "S" + rand,
68
        BOOL = "B" + rand,
69
        ERR = "E" + rand,
70
        COLL = "C" + rand;
71
72
    function reconvert(str) {
73
        str = str.replace(/(\\u)(\w{1,4})/gi, function($0) {
74
            return (String.fromCharCode(parseInt((escape($0).replace(/(%5Cu)(\w{1,4})/g, "$2")), 16)));
75
        });
76
        str = str.replace(/(&#x)(\w{1,4});/gi, function($0) {
77
            return String.fromCharCode(parseInt(escape($0).replace(/(%26%23x)(\w{1,4})(%3B)/g, "$2"), 16));
78
        });
79
        str = str.replace(/(&#)(\d{1,6});/gi, function($0) {
80
            return String.fromCharCode(parseInt(escape($0).replace(/(%26%23)(\d{1,6})(%3B)/g, "$2")));
81
        });
82
83
        return str;
84
    }
85
86
    function units(size) {
87
        return size > 1048576 ? (0 | (size / 1048576)) + "MB" :
88
            size > 1024 ? (0 | (size / 1024)) + "KB" :
89
            size + "B";
90
    }
91
92
    function fragment(a, b) {
93
        var frag = document.createDocumentFragment();
94
        frag.appendChild(document.createTextNode(a));
95
        if (b) {
96
            frag.appendChild(div.cloneNode());
97
            frag.appendChild(document.createTextNode(b));
98
        } else {
99
            frag.appendChild(document.createElement("br"));
100
        }
101
        return frag;
102
    }
103
104
    function change(node, query, name, set) {
105
        var list = node.querySelectorAll(query),
106
            i = list.length;
107
        for (; i--;) list[i].classList[set ? "add" : "remove"](name);
0 ignored issues
show
Coding Style Best Practice introduced by
Curly braces around statements make for more readable code and help prevent bugs when you add further statements.

Consider adding curly braces around all statements when they are executed conditionally. This is optional if there is only one statement, but leaving them out can lead to unexpected behaviour if another statement is added later.

Consider:

if (a > 0)
    b = 42;

If you or someone else later decides to put another statement in, only the first statement will be executed.

if (a > 0)
    console.log("a > 0");
    b = 42;

In this case the statement b = 42 will always be executed, while the logging statement will be executed conditionally.

if (a > 0) {
    console.log("a > 0");
    b = 42;
}

ensures that the proper code will be executed conditionally no matter how many statements are added or removed.

Loading history...
108
    }
109
110
    function changeSiblings(node, name, set) {
111
        var tmp, i = 0,
112
            query = [];
113
114
        for (; node && node.tagName === "I";) {
115
            tmp = node.previousElementSibling;
116
            if (tmp && tmp.className == KEY) {
117
                query.unshift(".D" + rand + ">i.I" + rand + "[data-key='" + node.dataset.key + "']");
118
            } else if (query[0]) {
119
                query.unshift(".D" + rand + ">i.I" + rand);
120
            } else {
121
                for (; tmp; tmp = tmp.previousElementSibling)
122
                    if (tmp.tagName === "BR") i++;
0 ignored issues
show
Coding Style Best Practice introduced by
Curly braces around statements make for more readable code and help prevent bugs when you add further statements.

Consider adding curly braces around all statements when they are executed conditionally. This is optional if there is only one statement, but leaving them out can lead to unexpected behaviour if another statement is added later.

Consider:

if (a > 0)
    b = 42;

If you or someone else later decides to put another statement in, only the first statement will be executed.

if (a > 0)
    console.log("a > 0");
    b = 42;

In this case the statement b = 42 will always be executed, while the logging statement will be executed conditionally.

if (a > 0) {
    console.log("a > 0");
    b = 42;
}

ensures that the proper code will be executed conditionally no matter how many statements are added or removed.

Loading history...
123
                query.unshift(".D" + rand + ">" + (i ? "br:nth-of-type(" + i + ")+i.I" + rand : "i.I" + rand + ":first-child"));
124
            }
125
            node = node.parentNode && node.parentNode.previousElementSibling;
126
        }
127
        if (!query[1]) return;
0 ignored issues
show
Coding Style Best Practice introduced by
Curly braces around statements make for more readable code and help prevent bugs when you add further statements.

Consider adding curly braces around all statements when they are executed conditionally. This is optional if there is only one statement, but leaving them out can lead to unexpected behaviour if another statement is added later.

Consider:

if (a > 0)
    b = 42;

If you or someone else later decides to put another statement in, only the first statement will be executed.

if (a > 0)
    console.log("a > 0");
    b = 42;

In this case the statement b = 42 will always be executed, while the logging statement will be executed conditionally.

if (a > 0) {
    console.log("a > 0");
    b = 42;
}

ensures that the proper code will be executed conditionally no matter how many statements are added or removed.

Loading history...
128
        query[0] = ".R" + rand + ">i.I" + rand;
129
        change(document, query.join("+"), name, set);
130
    }
131
132
    function keydown(e) {
133
        if (hovered) {
134
            e.preventDefault();
135
            if (e.altKey) {
136
                changeSiblings(hovered, HOV, 1);
137
            } else if (e[mod]) {
138
                change(hovered.nextSibling, "i.I" + rand, HOV, 1);
139
            }
140
        }
141
    }
142
143
    function init() {
144
        tag = document.createElement("style");
145
        tag.textContent = [
146
            '.R', ',.D', '{font:16px ' + fontStyle + '}' +
147
            '.D', '{margin-left:6px; padding-left:1em; margin-top: 1px; border-left:1px dashed; border-color: #93A1A1;}' +
148
            '.X', '{border:1px solid #ccc; padding:1em}' +
149
            'a.L', '{text-decoration:none}' +
150
            'a.L', ':hover,a.L', ':focus{text-decoration:underline}' +
151
            'i.I', '{cursor:pointer;color:#ccc}' +
152
            'i.H', ',i.I', ':hover{text-shadow: 1px 1px 3px #999; color:#333}' +
153
            'i.I', ':before{content:" ▼ "}' +
154
            'i.C', ':before{content:" ▶ "}' +
155
            'i.I', ':after{content:attr(data-content)}' +
156
            'i.C', '+.D', '{width:1px; height:1px; margin:0; padding:0; border:0; display:inline-block; overflow:hidden}' +
157
            '.S', '{color:' + strColor + '}' + // string
158
            '.K', '{color:' + keyColor + '}' + // key
159
            '.E', '{color:#BCADAD}' + // error
160
            '.B', '{color:' + intColor + '}' + // number and bool
161
            '.E', ',.B', '{font-style: italic}' + // number bold
162
            'h3.E', '{margin:0 0 1em}'
163
        ].join(rand);
164
165
        tag.textContent = tag.textContent + 'body {background: ' + bgColor + '; color:' + defaultColor + ';}';
166
167
        div.classList.add(DIV);
168
        document.head.appendChild(tag);
169
        document.addEventListener("keydown", keydown);
170
        document.addEventListener("keyup", function(e) {
0 ignored issues
show
Unused Code introduced by
The parameter e is not used and could be removed.

This check looks for parameters in functions that are not used in the function body and are not followed by other parameters which are used inside the function.

Loading history...
171
            if (hovered) change(document, "." + HOV, HOV);
0 ignored issues
show
Coding Style Best Practice introduced by
Curly braces around statements make for more readable code and help prevent bugs when you add further statements.

Consider adding curly braces around all statements when they are executed conditionally. This is optional if there is only one statement, but leaving them out can lead to unexpected behaviour if another statement is added later.

Consider:

if (a > 0)
    b = 42;

If you or someone else later decides to put another statement in, only the first statement will be executed.

if (a > 0)
    console.log("a > 0");
    b = 42;

In this case the statement b = 42 will always be executed, while the logging statement will be executed conditionally.

if (a > 0) {
    console.log("a > 0");
    b = 42;
}

ensures that the proper code will be executed conditionally no matter how many statements are added or removed.

Loading history...
172
        })
173
        document.addEventListener("mouseover", function(e) {
174
            if (e.target.tagName === "I") {
175
                hovered = e.target;
176
                keydown(e);
177
            }
178
        })
179
        document.addEventListener("mouseout", function(e) {
0 ignored issues
show
Unused Code introduced by
The parameter e is not used and could be removed.

This check looks for parameters in functions that are not used in the function body and are not followed by other parameters which are used inside the function.

Loading history...
180
            if (hovered) {
181
                change(document, "." + HOV, HOV);
182
                hovered = null;
183
            }
184
        })
185
    }
186
187
    function draw(str, to, first, box) {
188
        tag || init();
189
190
        var re = /("(?:((?:https?|file):\/\/(?:\\?\S)+?)|(?:\\?.)*?)")\s*(:?)|-?\d+\.?\d*(?:e[+-]?\d+)?|true|false|null|[[\]{},]|(\S[^-[\]{},"\d]*)/gi,
191
            node = div.cloneNode(),
192
            link = document.createElement("a"),
193
            span = document.createElement("span"),
194
            info = document.createElement("i"),
195
            colon = document.createTextNode(": "),
196
            comma = fragment(","),
197
            path = [],
198
            cache = {
199
                "{": fragment("{", "}"),
200
                "[": fragment("[", "]")
201
            };
202
203
        node.className = "R" + rand + (box ? " " + box : "");
204
205
        link.classList.add("L" + rand);
206
        info.classList.add("I" + rand);
207
208
        to.addEventListener("click", function(e) {
209
            var target = e.target,
210
                open = target.classList.contains(COLL);
211
            if (target.tagName == "I") {
212
                if (e.altKey) {
213
                    changeSiblings(target, COLL, !open);
214
                } else if (e[mod]) {
215
                    open = target.nextSibling.querySelector("i");
216
                    if (open) change(target.nextSibling, "i", COLL, !open.classList.contains(COLL));
0 ignored issues
show
Coding Style Best Practice introduced by
Curly braces around statements make for more readable code and help prevent bugs when you add further statements.

Consider adding curly braces around all statements when they are executed conditionally. This is optional if there is only one statement, but leaving them out can lead to unexpected behaviour if another statement is added later.

Consider:

if (a > 0)
    b = 42;

If you or someone else later decides to put another statement in, only the first statement will be executed.

if (a > 0)
    console.log("a > 0");
    b = 42;

In this case the statement b = 42 will always be executed, while the logging statement will be executed conditionally.

if (a > 0) {
    console.log("a > 0");
    b = 42;
}

ensures that the proper code will be executed conditionally no matter how many statements are added or removed.

Loading history...
217
                } else {
218
                    target.classList[open ? "remove" : "add"](COLL);
219
                }
220
            }
221
        }, true);
222
223
        to.replaceChild(box = node, first);
224
        loop(str, re);
225
226
        function loop(str, re) {
227
            str = reconvert(str);
228
            var match, val, tmp, i = 0,
229
                len = str.length;
230
            try {
231
                for (; match = re.exec(str);) {
232
                    val = match[0];
233
                    if (val == "{" || val == "[") {
234
                        path.push(node);
0 ignored issues
show
Bug introduced by
The variable node is changed as part of the for loop for example by node.lastChild.previousSibling on line 236. Only the value of the last iteration will be visible in this function if it is called after the loop.
Loading history...
235
                        node.appendChild(cache[val].cloneNode(true));
236
                        node = node.lastChild.previousSibling;
237
                        node.len = 1;
238
                        node.start = re.lastIndex;
239
                    } else if ((val == "}" || val == "]") && node.len) {
240
                        if (node.childNodes.length) {
241
                            tmp = info.cloneNode();
242
                            tmp.dataset.content = node.len + (
243
                                node.len == 1 ?
244
                                (val == "]" ? " item, " : " property, ") :
245
                                (val == "]" ? " items, " : " properties, ")
246
                            ) + units(re.lastIndex - node.start + 1);
247
248
                            if ((val = node.previousElementSibling) && val.className == KEY) {
249
                                tmp.dataset.key = reconvert(val.textContent.slice(1, -1).replace(/'/, "\\'"));
250
                            }
251
                            node.parentNode.insertBefore(tmp, node);
252
                        } else {
253
                            node.parentNode.removeChild(node);
254
                        }
255
                        node = path.pop();
256
                    } else if (val == ",") {
257
                        node.len += 1;
258
                        node.appendChild(comma.cloneNode(true));
259
                    } else {
260
                        if (match[2]) {
261
                            tmp = link.cloneNode();
262
                            tmp.href = match[2].replace(/\\"/g, '"');
263
                        } else {
264
                            tmp = span.cloneNode();
265
                        }
266
                        tmp.textContent = match[1] || val;
267
                        tmp.classList.add(match[3] ? KEY : match[1] ? STR : match[4] ? ERR : BOOL);
268
                        node.appendChild(tmp);
269
                        if (match[3]) {
270
                            node.appendChild(colon.cloneNode());
271
                        }
272
                    }
273
                    if (++i > 1000) {
274
                        document.title = (0 | (100 * re.lastIndex / len)) + "% of " + units(len);
275
                        return setTimeout(function() {
276
                            loop(str, re)
277
                        });
278
                    }
279
                }
280
                document.title = ""
281
                JSON.parse(str)
0 ignored issues
show
Best Practice introduced by
There is no return statement in this branch, but you do return something in other branches. Did you maybe miss it? If you do not want to return anything, consider adding return undefined; explicitly.
Loading history...
282
            } catch (e) {
283
                tmp = document.createElement("h3");
284
                tmp.className = ERR;
285
                tmp.textContent = e;
286
                box.insertBefore(tmp, box.firstChild);
0 ignored issues
show
Best Practice introduced by
There is no return statement in this branch, but you do return something in other branches. Did you maybe miss it? If you do not want to return anything, consider adding return undefined; explicitly.
Loading history...
287
            }
288
        }
289
    }
290
291
    if (strictOnly) {
292
        // only render when the contentType is json
293
        if (/[+\/]json$/i.test(document.contentType)) {
294
            draw(str, body, first)
0 ignored issues
show
Bug introduced by
The variable str seems to be never initialized.
Loading history...
295
        }
296
    } else {
297
        // check whether the content is json or like json
298
        if (first &&
299
            (first.tagName == "PRE" &&
300
                first == body.lastElementChild ||
301
                first == body.lastChild &&
302
                first.nodeType == 3) &&
303
            (str = first.textContent) &&
304
            (/[+\/]json$/i.test(document.contentType) ||
305
                (jsonpMatch = /^\s*((?:\/\*\*\/\s*)?([$a-z_][$\w]*)\s*(?:&&\s*\2\s*)?\()([^]+)(\)[\s;]*)$/i.exec(str)) &&
306
                jsonRe.test(jsonpMatch[3]) || jsonRe.test(str))) {
307
            if (jsonpMatch) {
308
                str = jsonpMatch[3]
309
                body.replaceChild(fragment(jsonpMatch[1], jsonpMatch[4]), first)
310
                first = body.lastChild.previousSibling
311
            }
312
            draw(str, body, first)
313
        }
314
    }
315
316
    chrome.runtime.onMessage.addListener(function(req, sender, sendResponse) {
0 ignored issues
show
Unused Code introduced by
The parameter sendResponse is not used and could be removed.

This check looks for parameters in functions that are not used in the function body and are not followed by other parameters which are used inside the function.

Loading history...
Unused Code introduced by
The parameter sender is not used and could be removed.

This check looks for parameters in functions that are not used in the function body and are not followed by other parameters which are used inside the function.

Loading history...
317
        var node,
318
            sel = window.getSelection(),
319
            range = sel.rangeCount && sel.getRangeAt(0),
320
            str = range && range.toString()
321
322
        if (!str) return
0 ignored issues
show
Coding Style Best Practice introduced by
Curly braces around statements make for more readable code and help prevent bugs when you add further statements.

Consider adding curly braces around all statements when they are executed conditionally. This is optional if there is only one statement, but leaving them out can lead to unexpected behaviour if another statement is added later.

Consider:

if (a > 0)
    b = 42;

If you or someone else later decides to put another statement in, only the first statement will be executed.

if (a > 0)
    console.log("a > 0");
    b = 42;

In this case the statement b = 42 will always be executed, while the logging statement will be executed conditionally.

if (a > 0) {
    console.log("a > 0");
    b = 42;
}

ensures that the proper code will be executed conditionally no matter how many statements are added or removed.

Loading history...
323
324
        if (req.op === "formatSelection") {
325
            node = document.createElement("div")
326
            range.deleteContents()
327
            range.insertNode(node)
328
            sel.removeAllRanges()
329
            draw(str, node.parentNode, node, "X" + rand)
330
        }
331
    })
332
}
333
334
var getting = browser.storage.local.get();
0 ignored issues
show
Bug introduced by
The variable browser seems to be never declared. If this is a global, consider adding a /** global: browser */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
335
getting.then(onGot, onError);
336